/*
Copyright 2010 Johan Maasing

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package nu.zoom.android.gdata.project.client;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.graphics.Typeface;
import android.net.Uri;
import android.text.SpannableStringBuilder;
import android.text.style.CharacterStyle;
import android.text.style.StyleSpan;
import android.util.Log;

/**
 * @author Johan Maasing
 * 
 */
public class UIUtilities {
	private static final String LOG_TAG = WKC.LOG_TAG;

	public static void reportError(final Activity context, final int errorID,
			final Exception exception) {
		reportError(context, context.getText(errorID), exception);
	}

	public static void reportError(final Activity activity,
			final CharSequence text, final Exception exception) {
		Log.e(LOG_TAG, "Fail", exception);
		activity.runOnUiThread(new Runnable() {
			public void run() {
				String message = text.toString();
				if (exception != null) {
					message = message + "\n" + exception.getLocalizedMessage();
				}
				final AlertDialog.Builder builder = new AlertDialog.Builder(
						activity);
				builder.setCancelable(true).setMessage(message)
						.setTitle(R.string.error);
				final AlertDialog alertDialog = builder.create();
				alertDialog.show();
			}
		});

	}

	public static CharSequence plainifyMarkup(String html) {
		String result = html;
		// result = result.replaceAll("<li>", "*");
		// result = result.replaceAll("</li>", "");
		// result = result.replaceAll("<ul>", "\n");
		// result = result.replaceAll("</ul>", "");
		result = result.replaceAll("&quot;", "\"");
		result = result.replaceAll("&lt;", "<");
		result = result.replaceAll("&gt;", ">");
		result = result.replaceAll("&amp;", "&");

		return setSpanBetweenTokens(result, "<i>", "</i>", new StyleSpan(Typeface.ITALIC));
	}

	/**
	 * Given either a Spannable String or a regular String and a token, apply
	 * the given CharacterStyle to the span between the tokens, and also remove
	 * tokens.
	 * <p>
	 * For example, {@code setSpanBetweenTokens("Hello ##world##!", "##",
	 * new ForegroundColorSpan(0xFFFF0000));} will return a CharSequence
	 * {@code "Hello world!"} with {@code world} in red.
	 * 
	 * @param text
	 *            The text, with the tokens, to adjust.
	 * @param startToken
	 *            The token string before styling; there MUST be at matching
	 *            endToken in the string or null will be returned.
	 * @param endToken
	 *            The token string after styling; there MUST be at matching
	 *            startToken in the string or null will be returned.
	 * @param cs
	 *            The style to apply to the CharSequence. WARNING: You cannot
	 *            send the same two instances of this parameter, otherwise the
	 *            second call will remove the original span.
	 * @return A Spannable CharSequence with the new style applied. Returns null
	 *         if tokens doe not add up.
	 * 
	 * @see http
	 *      ://developer.android.com/reference/android/text/style/CharacterStyle
	 *      .html
	 * @see http
	 *      ://www.androidengineer.com/2010/08/easy-method-for-formatting-android
	 *      .html
	 */
	public static CharSequence setSpanBetweenTokens(CharSequence text,
			final String startToken, final String endToken,
			final CharacterStyle... cs) {
		// Copy the spannable string to a mutable spannable string
		SpannableStringBuilder ssb = new SpannableStringBuilder(text);

		// Start and end refer to the points where the span will apply
		int start = -1 ;
		int end = -1 ;
		do {
			int startTokenLen = startToken.length();
			int endTokenLen = endToken.length();
			start = ssb.toString().indexOf(startToken) + startTokenLen;
			end = ssb.toString().indexOf(endToken, start);
			if (start > -1 && end > -1) {
				for (CharacterStyle c : cs) {
					ssb.setSpan(c, start, end, 0);
				}
				// Delete the tokens before and after the span
				ssb.delete(end, end + endTokenLen);
				ssb.delete(start - startTokenLen, start);
			}
		} while (start > -1 && end > -1);
		return ssb;
	}

	public static abstract class FeedRunner<T, P> {
		protected final AtomicBoolean runningQuery = new AtomicBoolean();
		private final Activity activity;

		public FeedRunner(final Activity activity) {
			super();
			this.activity = activity;
		}

		public void run(final Uri feedUri, final P parameter) {
			if (runningQuery.compareAndSet(false, true)) {
				final CharSequence title = activity
						.getText(R.string.progress_feed_title);
				final CharSequence message = activity
						.getText(R.string.progress_feed_message)
						+ " "
						+ feedUri.toString();
				final ProgressDialog progressDialog = ProgressDialog.show(
						activity, title, message);
				final Thread getThread = new Thread(new Runnable() {
					@Override
					public void run() {
						try {
							final T result = runInBackground(feedUri, parameter);
							activity.runOnUiThread(new Runnable() {
								@Override
								public void run() {
									result(result);
								}
							});
						} catch (IOException e) {
							UIUtilities.reportError(activity,
									R.string.error_feed, e);
						} finally {
							progressDialog.dismiss();
							runningQuery.set(false);
						}
					}
				}, "Get " + feedUri.toString());
				getThread.start();
			} else {
				Log.d(LOG_TAG,
						"Feed query already running, ignoring request to get feed: "
								+ feedUri);
			}
		}

		protected abstract void result(T result);

		protected abstract T runInBackground(Uri uri, P parameter)
				throws IOException;
	}
}
